Skip to content

fix(realunit): offer NewRegistration empty form for first-time users instead of KycRequired#3836

Merged
TaprootFreak merged 2 commits into
DFXswiss:developfrom
joshuakrueger-dfx:fix/realunit-registration-first-time-empty-form
Jun 8, 2026
Merged

fix(realunit): offer NewRegistration empty form for first-time users instead of KycRequired#3836
TaprootFreak merged 2 commits into
DFXswiss:developfrom
joshuakrueger-dfx:fix/realunit-registration-first-time-empty-form

Conversation

@joshuakrueger-dfx

@joshuakrueger-dfx joshuakrueger-dfx commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Problem

First-time RealUnit onboarding dead-ends. The app's Onboarding process screen shows:

Error while loading — An error occurred while loading: Please complete your identity verification first. …

This is the client's KycRequiredFailure, triggered when GET /v1/realunit/registration returns state: KycRequired.

Root cause

RealunitService.getRegistrationInfo() returns KycRequired whenever there is no existing registration step and toUserDataDtoFromUserData() returns undefined — i.e. !firstname && !surname. That is exactly the first-time user (email registered → KYC Level 10, but no name on file yet).

The client treats KycRequired as a terminal "complete your identity verification first" failure, so onboarding can never start — even though this is precisely the user the registration flow exists to onboard:

  • completeRegistration() requires only KYC Level 10 + email and, for users with no prior data (!hasExistingData), persists the manually-entered form data (updatePersonalData(userData, dto.kycData)).
  • The method's own comment already states the intent: "Without verified personal data there is nothing useful to pre-fill — the app will continue to collect every field manually." — i.e. show an empty form, not a dead-end.
  • The client already handles NewRegistration with no userData by rendering an empty form (covered by its cubit + tests).

The KycRequired branch was introduced in #3782 (whose goal was only to pre-fill the form from existing KYC data) and unintentionally turned the previous empty-form path into a dead-end.

Fix

When no registration step exists, always return NewRegistration — pre-filled with userData when verified personal data is present, otherwise without userData so the client renders an empty form and collects every field manually. The existing prefill path is unchanged.

KycRequired is removed from RealUnitRegistrationState: nothing emits it anymore, and per the repo's API Capability Design guideline (YAGNI for enum members — don't ship future-proof enum values without a current backend gate that emits them), keeping it would be dead code. Re-adding it the day a genuine "KYC blocked" gate actually throws is additive and backwards-compatible.

Changes

  • realunit.service.ts — collapse the getRegistrationInfo fallthrough to a single NewRegistration return (userData = prefill or undefined); update comments.
  • realunit-registration.dto.ts — document NewRegistration as covering prefilled and empty; remove the unemitted KycRequired enum member and its mention in the state description.
  • realunit.controller.ts — drop KycRequired from the routed-states list in the endpoint description.
  • realunit.service.spec.ts — the no-data case now asserts NewRegistration with no userData (first-time user → empty form).

Test

getRegistrationInfo
  ✓ returns state=ALREADY_REGISTERED …
  ✓ returns state=ADD_WALLET …
  ✓ returns state=NEW_REGISTRATION when no step exists but userData has firstname/surname
  ✓ returns state=NEW_REGISTRATION with no userData when no step exists and no KYC data is present (first-time user gets an empty form)
  ✓ defaults swissTaxResidence to false …
  ✓ falls back to EN …

Frontend synchronization

The realunit-app still has KycRequiredFailure handling for this state. The API now never emits it and drops it from the contract, so that client branch is dead and should be removed in the paired app PR.

Note

Touches the same files as #3786 (registration → aktionariat_registration entity refactor), which does not address this KycRequired dead-end. Whichever merges first, the other will need a small rebase.

…instead of KycRequired

getRegistrationInfo dead-ended onboarding for any wallet without pre-fillable
KYC data: it returned KycRequired, which the client surfaces as a terminal
"complete your identity verification first" failure — yet that is exactly the
first-time user the registration flow exists to onboard. completeRegistration
requires only KYC Level 10 (email) and persists manually-entered data when no
prior data exists, so the form must be offered, not withheld.

Return NewRegistration with userData omitted (empty form) when neither a
registration step nor pre-fill data exist; the existing prefill path is
unchanged. KycRequired is kept as a reserved, no-longer-emitted contract value
so clients retain explicit handling. Regression introduced in DFXswiss#3782.

@TaprootFreak TaprootFreak left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

@TaprootFreak TaprootFreak merged commit e020b1d into DFXswiss:develop Jun 8, 2026
5 checks passed
TaprootFreak added a commit to RealUnitCH/app that referenced this pull request Jun 8, 2026
## Summary
Backend [DFXswiss/api#3836](DFXswiss/api#3836)
drops `KycRequired` from `RealUnitRegistrationState` (YAGNI — it is no
longer emitted by `GET /v1/realunit/registration`). This removes the
now-unreachable consumer-side handling so the app stops carrying dead
code for a state the API never sends.

## Changes
- `real_unit_registration_state.dart` — remove the
`kycRequired(jsonName: 'KycRequired')` enum value.
- `kyc_cubit.dart` — remove the `case
RealUnitRegistrationState.kycRequired` dispatch arm.
- `kyc_state.dart` — remove the `KycRequiredFailure` state class.
- `kyc_page_manager.dart` — remove the `KycRequiredFailure()` →
`KycFailurePage` switch arm.
- `real_unit_registration_info_dto.dart` — drop the stale `kycRequired`
mention from the doc comment.
- `strings_de.arb` / `strings_en.arb` — remove the unused
`kycRequiredFailureMessage` key.
- `docs/wallet-modes.md` — drop the registration `KycRequired`
references.
- Tests — remove the obsolete `KycRequired` parse test and the
`KycRequiredFailure` cubit test.

## Note
`PaymentInfoError.kycRequired` (buy/sell) and the transaction-status
`kycRequired` are unrelated concepts and remain untouched. After this
change, an unexpected `state: 'KycRequired'` payload would throw
`ArgumentError` (covered by the existing unknown-state test) —
acceptable since the API no longer emits it.

## Test plan
- [x] `flutter analyze` — clean (only a pre-existing, unrelated
generated-i18n warning present on `staging`)
- [x] `flutter test` — 2272 non-golden tests pass
- [ ] Visual Regression / goldens via CI (no golden references the
removed state)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants